<script setup lang="ts">
import * as cornerstone from '@cornerstonejs/core'
import * as cornerstoneTools from '@roos/cornerstone3d-tools'
const { RenderingEngine, Enums: cnEnums, volumeLoader, setVolumesForViewports, cache, getRenderingEngine } = cornerstone
const { ViewportType } = cnEnums
const { ToolGroupManager, PanTool, ZoomTool, TrackballRotateTool, Enums: csToolsEnums } = cornerstoneTools;
const { MouseBindings } = csToolsEnums;
import Selector from './components/selector/index.vue'
import { RefreshRight, CloseBold } from '@element-plus/icons-vue'
import { vec3, mat4 } from 'gl-matrix';
import VConsole from 'vconsole';

const props = defineProps(['data', 'covers', 'activeIndex'])

const emits = defineEmits(['close'])

/*========================================>( 模型属性 )<========================================*/
//modal是否可见
const dialogVisible = ref<boolean>(false)
//当前显示哪个序列的图像
const activeIndex = ref<any>(0)
const isError = ref<boolean>(false)
//显示3d图像的质量
const quality = ref<number>(0)
//全局加载
const loading = ref<boolean>(false)

/*========================================>( dom属性 )<========================================*/
//展示3d图像的dom
const domView3d = ref()
const cvs3d = ref()

/*========================================>( 本地属性 )<========================================*/
const _renderingEngineId = '3D_ENGINE';
const _toolGroupId = '3D_GROUP';
let _volumeId: any = undefined
let _viewport: any = undefined
const imagePlaneModules:any = {}


/*========================================>( 普通方法 )<========================================*/
const initImagePlaneModules = () => {
    for(const {imageIds} of  props.data){
        for(const imageId of imageIds){
            const data = cornerstone.metaData.get('imagePlaneModule', imageId)
            imagePlaneModules[imageId] = data
        }
    }
}


//关闭本模块
const cancel = () => {
    clearCache()
    emits('close')
    vConsole.destroy();
}

//绑定工具
const bindTools = (toopGroup: any) => {
    toopGroup.addTool(PanTool.toolName);
    toopGroup.addTool(ZoomTool.toolName);
    toopGroup.addTool(TrackballRotateTool.toolName);
    toopGroup.setToolActive(PanTool.toolName, {
        bindings: [
            {
                mouseButton: MouseBindings.Auxiliary
            }
        ]
    });
    toopGroup.setToolActive(ZoomTool.toolName, {
        bindings: [
            {
                mouseButton: MouseBindings.Secondary
            }
        ]
    });
    toopGroup.setToolActive(TrackballRotateTool.toolName, {
        bindings: [
            {
                mouseButton: MouseBindings.Primary
            }
        ]
    });
}

//清除工具缓存
const clearCache = () => {
    const vs = cache.getVolumes()
    vs.forEach(v => {
        cache.removeVolumeLoadObject(v.volumeId)
    })
    const renderingEngine = getRenderingEngine(_renderingEngineId)
    if (renderingEngine) {
        renderingEngine.destroy()
    }
    const toolGroup = ToolGroupManager.getToolGroup(_toolGroupId)
    if (toolGroup) {
        ToolGroupManager.destroyToolGroup(_toolGroupId)
    }
}

const madeQuality = (value: number) => {
    const { actor } = _viewport.getActors()[0];
    const mapper = actor.getMapper();
    const image = mapper.getInputData();
    const dims = image.getDimensions();
    const spacing = image.getSpacing();
    const spatialDiagonal = vec3.length(
        vec3.fromValues(dims[0] * spacing[0], dims[1] * spacing[1], dims[2] * spacing[2])
    );

    let sampleDistance = spacing.reduce((a: any, b: any) => a + b) / 3.0;
    sampleDistance /= value > 1 ? 0.5 * value ** 2 : 1.0;
    const samplesPerRay = spatialDiagonal / sampleDistance + 1;
    mapper.setMaximumSamplesPerRay(samplesPerRay);
    mapper.setSampleDistance(sampleDistance);
    _viewport.render();
}

//初始化3d图像
const init3d = async () => {
    loading.value = true
    //检测是否有现成的渲染器和工具组
    const viewportId = '3D_VIEWPORT' + activeIndex.value;
    _volumeId = '3D_VOLUME' + activeIndex.value
    let renderingEngine = getRenderingEngine(_renderingEngineId)
    if (!renderingEngine) {
        renderingEngine = new RenderingEngine(_renderingEngineId)
    }
    let toolGroup = ToolGroupManager.getToolGroup(_toolGroupId)
    if (!toolGroup) {
        toolGroup = ToolGroupManager.createToolGroup(_toolGroupId);
        bindTools(toolGroup)
    }
    
    const { imageIds } = props.data[activeIndex.value]
    // console.log('imageIds', imageIds)
    // const { imageIds } = props.data[1]
    const imagePlaneMetaData = cornerstone.metaData.get('imagePlaneModule', imageIds[0]);
    console.log('jsiadhgjhsda',imagePlaneMetaData)
    const element = Array.isArray(cvs3d.value) ? cvs3d.value[0] : cvs3d.value
    renderingEngine.setViewports([
        {
            viewportId,
            type: ViewportType.VOLUME_3D,
            element,
            defaultOptions: {
                orientation: cnEnums.OrientationAxis.CORONAL
            }
        },
    ]);
    try {
        // await volumeLoader.createAndCacheVolumeFromImages(_volumeId, imageIds);
        const volume = await volumeLoader.createAndCacheVolume(_volumeId, {
            imageIds,
        });
        volume.load();
    } catch (error) {
        console.log('发生错误', error)
        isError.value = true
        clearCache()
        loading.value = false
    }
    try {
        await setVolumesForViewports(renderingEngine, [{ volumeId: _volumeId }], [viewportId])
    } catch (error) {
        console.log('发生错误', error)
        isError.value = true
        clearCache()
        loading.value = false
    }
    _viewport = renderingEngine.getViewport(viewportId);//createAndCacheVolumeFromImages方法不需要 _volume.load();
    console.log('_viewport', _viewport, viewportId)
    _viewport.setProperties({
        preset: 'CT-AAA'
    });
    _viewport.render();
    toolGroup?.addViewport(viewportId, _renderingEngineId);
    const { actor } = _viewport.getActors()[0];
    const mapper = actor.getMapper();
    const image = mapper.getInputData();
    const spacing = image.getSpacing();
    const sampleDistance = mapper.getSampleDistance();
    const averageSpacing = spacing.reduce((a: any, b: any) => a + b) / 3.0;
    if (sampleDistance === averageSpacing) {
        quality.value = 1;
    } else {
        quality.value = Math.sqrt(averageSpacing / (sampleDistance * 0.5));
    }
    madeQuality(quality.value);
    loading.value = false
}

/*========================================>( 监听方法 )<========================================*/
//监听序列变化
const handleSeriesChanged = async (index: number) => {
    isError.value = false
    activeIndex.value = index
    nextTick(async () => {
        await init3d()
    })
}

//监听3d重置
const handleReset = () => {
    _viewport.resetCamera()
    _viewport.render();
}

//监听图像治疗变化
const handleQualityChanged = (e: any) => {
    console.log('质量变化', e)
    madeQuality(e)
}

/*========================================>( 钩子 )<========================================*/
activeIndex.value = props.activeIndex
dialogVisible.value = true

onMounted(() => {
    window.onerror = function (message, source, lineno, colno, error) {
        console.log('发生错误')
        isError.value = true
        clearCache()
        loading.value = false
        return true
    };
    window.addEventListener('resize', () => {
        const renderingEngine = getRenderingEngine(_renderingEngineId)
        renderingEngine?.resize()
    })
    initImagePlaneModules()
    cornerstone.metaData.addProvider((type, imageId)=>{
        if(type=="imagePlaneModule"){
            const data = imagePlaneModules[imageId]
            console.log('找到数据',imageId,data)
            return data
        }
        return undefined
    });
    nextTick(async () => {
        await init3d()
    })
})

const vConsole = new VConsole({ theme: 'dark' });

</script>
<template>
    <el-dialog class="view3d" ref="domView3d" v-model="dialogVisible" width="95%" @close="cancel" align-center>
        <template #header>
            <div class="text-center text-[#fff]">3D视图</div>
        </template>
        <div class="content">
            <div class="series">
                <Selector :activeIndex="activeIndex" :covers="props.covers" :loading="loading"
                    @change="handleSeriesChanged" />
            </div>
            <div v-if="!isError" class="dom-cvs" ref="cvs3d">
            </div>
            <div v-else class="error">当前序列不支持3d视图</div>
        </div>

        <template #footer>
            <div class="dialog-footer">
                <div class="float-left text-[#ccc] m-r-110-0 f-14">
                    <span>[⭐右键拖动：放大缩小]</span>
                    <span>[⭐左键拖动：转换镜头方向] </span>
                    <span>[⭐滑轮按下拖动：移动图像]</span>
                </div>
                <el-form label-width="auto" inline>
                    <el-form-item>
                        <el-button circle :icon="RefreshRight" type="primary" @click="handleReset"></el-button>
                    </el-form-item>
                    <el-form-item label="图像质量">
                        <el-slider v-model="quality" style="width:120px" :min="1" :max="4" @change="handleQualityChanged"
                            :disabled="loading" />
                    </el-form-item>
                    <el-form-item>
                        <el-button @click="cancel" round type="primary" plain :icon="CloseBold">关闭视图</el-button>
                    </el-form-item>
                </el-form>
            </div>
        </template>
    </el-dialog>
</template>
<style lang="scss">
.el-dialog.view3d {
    background-color: #2d2d30;
    position: relative;
    border: solid 1px #666;
    height: 95vh;

    .el-dialog__body {
        padding: 0 10px;
    }

    .el-dialog__title {
        color: #fff !important;
        font-size: 15px;
    }

    .el-dialog__header {
        height: 60px;
        padding-bottom: 0;
    }

    .el-dialog__wrapper {
        display: table;
    }

    .content {
        width: 100%;
        height: calc(95vh - 150px);
        display: flex;
        flex-direction: row;

        .series {
            width: 110px;
            height: 100%;
        }

        .dom-cvs {
            flex: 1;
            height: 100%;
            background-color: #000;
        }

        .error {
            flex: 1;
            height: 100%;
            background-color: #000;
            line-height: 600px;
            text-align: center;
            color: #fff;
        }
    }



}
</style>
<style lang="scss" scoped></style>